/*
 * Main GUI class (MVC view)
 */
package javaapplication6.GUI;

import java.awt.BorderLayout;
import java.awt.Color;
import static java.awt.Component.CENTER_ALIGNMENT;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javaapplication6.CONTROLLER.MonteCarloSimulationController;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;

/**
 *
 * @author mathieubakrim
 */
public class MonteCarloGUI extends JFrame{
    private static final long serialVersionUID = 1L;
    
    /**
     * Instantiate a controller instance to allow exchange between the model and the view
     */
    protected MonteCarloSimulationController theController = new MonteCarloSimulationController();
    
    
    private final String[] strJlb = {   "Share price of the day (s)",
                                        "Strike price (x)",
                                        "Remaining time to option maturity (t)",
                                        "Interest without risk (r)",
                                        "Carry rate cost of the option (b)",
                                        "Volatility (v)",
                                        "nSteps",
                                        "nSimulations"
                                    };
    
    private final String[] strJtf = {"s", "x", "t", "r", "b", "v", "nSteps", "nSimulations"}; // jtextfield denomination used for simulation parameters
    private final char [] chrFlag = {'c', 'p'}; // call or put (option type)
    private final char [] chrArg = {'1', '2', '3', '4', '5'}; // presets use
    
    /**
     *  identifier of a selected set of preset among chrArg values
     */
    protected int set; 
    
    private final JPanel thePanel = new JPanel();
    private final JPanel theCenterPanel = new JPanel();
    private final JPanel theNorthPanel = new JPanel();
    private final JPanel theEastPanel = new JPanel();
    private final JPanel theSouthPanel = new JPanel();
        
    private JTextField[] tabJtf;
    
    private JLabel jlbThread;
    private JLabel[] tabJlb;
    private JLabel title;
    private JLabel flag;
    private JLabel arg;
    private JLabel resultLbl;
    private JLabel result;
    private JLabel executionTime;
    private JLabel executionTimeLbl;
    
    private JComboBox jcbFlag;
    private JComboBox jcbArg;
    private JComboBox jcbThread;
    
    private JMenuBar menuBar; 
    private JMenu services;
    private JMenu help;

    private JMenuItem quit;
    private JMenuItem changeArg;    
    private JMenuItem credits;
    
    private JButton ok;
    private JButton stop;
    private JButton pause;
    
    private Task task;
    private JProgressBar progressBar; 
    private boolean pauseProcessing;
    private boolean stopProcessing;
    
    
    /**
     * GUI constructor
     * Calling various method to initialize each important component of the GUI
     * @throws FileNotFoundException
     * @throws IOException 
     */
    public MonteCarloGUI() throws FileNotFoundException, IOException{
        this.setTitle("MonteCarloSimulation application");
        this.setSize(700, 400);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        this.setResizable(false);
        this.setContentPane(thePanel);
        thePanel.setLayout(new BorderLayout());
         
        initMenu();             // the window Menu
        initTheNorthPanel();    // the title panel
        initTheCenterPanel();   // the central panel with monte carlo parameters (labels + inputs)
        initTheSouthPanel();    // computing time and progress bar
        initTheEastPanel();     // processing buttons and thread mode
        
        thePanel.add(theNorthPanel, BorderLayout.NORTH);
        thePanel.add(theCenterPanel, BorderLayout.CENTER);
        thePanel.add(theSouthPanel, BorderLayout.SOUTH);
        thePanel.add(theEastPanel, BorderLayout.EAST);
        this.setVisible(true);        
    }

    /**
     * Initialize the menu
     */
    private void initMenu(){
        menuBar = new JMenuBar();
        services = new JMenu("Services");
        help = new JMenu("Help");
        changeArg = new JMenuItem("Change argument set values");
        credits = new JMenuItem("Credits");
        quit = new JMenuItem("Quit");
        
        services.add(changeArg);
        services.addSeparator();
        services.add(quit);
        help.add(credits);
         
        quit.addActionListener(new QuitListener());
        changeArg.addActionListener(new ChangeArgListener());
        credits.addActionListener(new CreditsListener());
        
        menuBar.add(services);
        menuBar.add(help);
        setJMenuBar(menuBar);
    }
    
    /**
     * Initialize the application title panel
     */
    private void initTheNorthPanel() {
        theNorthPanel.setLayout(new BorderLayout());       
        title = new JLabel("Monte Carlo calculator",SwingConstants.CENTER);
        Font boldFont=new Font(title.getFont().getName(),Font.BOLD,16); 
        title.setFont(boldFont);
        theNorthPanel.add(title, BorderLayout.CENTER);
        
        JSeparator jsp = new JSeparator();
        jsp.setBackground(Color.black);
        theNorthPanel.add(jsp, BorderLayout.SOUTH);                
    }

    /**
     * Initialize the main interactive panel (inputs)
     * @throws FileNotFoundException
     * @throws IOException 
     */
    private void initTheCenterPanel() throws FileNotFoundException, IOException {
        tabJtf = new JTextField[strJtf.length];
        tabJlb = new JLabel[strJlb.length];
        flag = new JLabel("Flag");
        arg = new JLabel("Set of arguments");
        jcbFlag = new JComboBox();
        jcbArg = new JComboBox();
        
        GridLayout gl = new GridLayout(10,2);
        gl.setHgap(5);
        gl.setVgap(5);
        theCenterPanel.setLayout(gl);
        theCenterPanel.setBackground(Color.lightGray);
        
        // Loop on JLabel array to display all the JLabels
        for(int j=0; j<strJlb.length;j++)
            tabJlb[j] = new JLabel(strJlb[j]);
        
        // Loop on JTextfield array to display all the JTextfields
        for(int k=0; k<strJtf.length;k++)
            tabJtf[k] = new JTextField();
       
        // Add flag combo box
       theCenterPanel.add(flag);
       theCenterPanel.add(jcbFlag);
       
       // Add argument set combo box
       theCenterPanel.add(arg);
       theCenterPanel.add(jcbArg);
        
       // Add Jlabel and JTextfield component into the container panel
        for(int i = 0; i<strJlb.length;i++){
            theCenterPanel.add(tabJlb[i]);
            theCenterPanel.add(tabJtf[i]);            
        }

        // Add flag combo box component into the container panel
        for(int l = 0; l<chrFlag.length; l++)
            jcbFlag.addItem(chrFlag[l]);
        
        // Add arg combo box component into the container panel
        for(int m = 0; m<chrArg.length; m++)
            jcbArg.addItem(chrArg[m]);

       // load the data
       theController.csvReader();
       jcbArg.addActionListener(new JcbListener());
       
       // select the right preset to display according to the arg combo box selected item
       jcbArg.setSelectedIndex(0);
    }

    /**
     * Initialize the output panel (results and progress bar)
     */
    private void initTheSouthPanel() {    
        resultLbl = new JLabel(" - ");
        result = new JLabel("Result:");
        executionTime = new JLabel("Executing time: ");
        executionTimeLbl = new JLabel(" - ");
                
        theSouthPanel.setLayout(new BoxLayout(theSouthPanel, BoxLayout.PAGE_AXIS));
        theSouthPanel.setBackground(Color.white);
        
        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);       
        
        JPanel b1 = new JPanel();
        b1.setLayout(new BoxLayout(b1, BoxLayout.LINE_AXIS));
        b1.add(new JSeparator());
        
        JPanel b2 = new JPanel();
        b2.setLayout(new BoxLayout((b2), BoxLayout.LINE_AXIS));
        b2.add(result);
        b2.add(resultLbl);
        
        JPanel b3 = new JPanel();
        b3.setLayout(new BoxLayout(b3, BoxLayout.LINE_AXIS));
        b3.add(executionTime);
        b3.add(executionTimeLbl);
        
        JPanel b4 = new JPanel();
        b4.setLayout(new BoxLayout(b4, BoxLayout.LINE_AXIS));
        b4.add(progressBar);

        
        b1.setBackground(Color.white);
        b2.setBackground(Color.white);
        b3.setBackground(Color.white);
        b4.setBackground(Color.white);
        
        theSouthPanel.add(b1);
        theSouthPanel.add(b2);
        theSouthPanel.add(b3);
        theSouthPanel.add(b4);
    }

    /**
     * Initialize the action panel (start button & threading mode)
     */
    private void initTheEastPanel() {
        jcbThread = new JComboBox();
        jlbThread = new JLabel("Mode");
        ok = new JButton("Compute");
        stop = new JButton(" Stop   ");
        pause = new JButton(" Pause  ");
        
        theEastPanel.setLayout(new BoxLayout((theEastPanel), BoxLayout.PAGE_AXIS));       
        
        ok.addActionListener(new ComputeListener());
        //stop.addActionListener(new ProgressBarListener());
        stop.addActionListener(new StopListener());
        stop.setAlignmentX(CENTER_ALIGNMENT);
        ok.setAlignmentX(CENTER_ALIGNMENT);
        pause.setAlignmentX(CENTER_ALIGNMENT);
        jcbThread.setAlignmentX(CENTER_ALIGNMENT);
        JPanel b1 = new JPanel();
        JPanel b2 = new JPanel();
        b2.setLayout(new FlowLayout());
        b2.setAlignmentX(CENTER_ALIGNMENT);
        b1.setLayout(new BoxLayout(b1, BoxLayout.PAGE_AXIS));        
        
        jcbThread.addItem("Single");
        jcbThread.addItem("Multi");
        b2.add(jlbThread);
        b2.add(jcbThread);
        b1.add(b2);
        b1.add(ok);
        b1.add(pause);
        b1.add(stop);

        theEastPanel.add(b1);          
    }
     
    /**
     * Swing worker task used to manage the multi threading in view
     * It also make the progress bar working
     */
    class Task extends SwingWorker<Void, Void>{

        @Override
        public Void doInBackground() throws InterruptedException, ExecutionException {

            int progress = 0;
            setProgress(0);
            
            while (progress < 100) {
                // Process                
                double computedValue = 0.0;
                if(stopProcessing == true) return null;
                computedValue = theController.computeSimulation(tabJtf, jcbFlag.getSelectedItem().toString(),
                         jcbArg.getSelectedItem().toString(), jcbThread.getSelectedItem().toString());
 
                long executionTime = theController.getExecutionTime(); //Execution time use
                resultLbl.setText(String.valueOf(computedValue));
                executionTimeLbl.setText(String.valueOf(executionTime) + "ms");
                progress = 100;
                setProgress(100);
            }
            return null;
        }
 
        /*
         * Executed in event dispatching thread
         */
        @Override
        public void done() {
            Toolkit.getDefaultToolkit().beep();
            stop.setEnabled(true);
            setCursor(null); //turn off the wait cursor
        }
    }
    
    /**
     * Listener associate with the start (ok) button
     * Call the simulation computing method through the swing worker instance
     */
    class ComputeListener  implements ActionListener, PropertyChangeListener {
        
          @Override
        public void actionPerformed(ActionEvent ae) {
            pauseProcessing = false;
            stopProcessing = false;
            progressBar.setIndeterminate(true);
            stop.setEnabled(true);
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            task = new Task();
            task.addPropertyChangeListener(this);
            task.execute();
        }

        @Override
        public void propertyChange(PropertyChangeEvent pce) {
            if ("progress" .equals(pce.getPropertyName())) {
                int progress = (Integer) pce.getNewValue();
                progressBar.setIndeterminate(false);
                progressBar.setValue(progress);
            } 
        }        
    }
    
    /**
     * Stop the processing
     */
    class StopListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent ae) {
            task.cancel(true);
            stopProcessing = true;
        }
    }
    
    /**
     * Modify the inputs values thanks to the selected preset
     */
    class JcbListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent ae) {
            set = Integer.parseInt(jcbArg.getSelectedItem().toString());
            set -=1;
            tabJtf[0].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getS()));
            tabJtf[1].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getX()));            
            tabJtf[2].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getT()));
            tabJtf[3].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getR()));           
            tabJtf[4].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getB()));
            tabJtf[5].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getV()));
            tabJtf[6].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getnSteps()));
            tabJtf[7].setText(String.valueOf(theController.getTheMonteCarloSimulationsPreset().get(set).getnSimulations()));  
        }  
    }
    
    /**
     * Listener associated to the quit option in the services menu
     */
    class QuitListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(null, "Have a nice day :)");
            System.exit(0);
        }
    }
    
    /**
     * Listener associated to the credit option in the help menu
     */
    class CreditsListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(null, "Monte Carlo Simulation Application \n"
                    + "ING4 ECE Project \n"
                    + "ADVANCED JAVA Engineering Course\n"
                    + "Authors : Mathieu Bakrim - Alexandre Czech");
            System.exit(0);
        }
    }
    
    /**
     * Listener associated to the service option that allow to modify the parameters values of a set
     */
    class ChangeArgListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent ae) {
            try {
                new ArgumentSetFrame(strJlb, strJtf, chrArg, theController);
            } catch (HeadlessException ex) {
                Logger.getLogger(MonteCarloGUI.class.getName()).log(Level.SEVERE, null, ex);
            } catch (FileNotFoundException ex) {
                Logger.getLogger(MonteCarloGUI.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(MonteCarloGUI.class.getName()).log(Level.SEVERE, null, ex);
            }      
        }
    } 
}
